package homeTask3;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;

public class FileMapper {
	private File input;
	private File output;
	private List<Integer> list;
	private byte[] buf;
	
	FileMapper(String input, String output){
		setNewFiles(input, output);
	}

	public File getInputFile(){
		return input;
	}
	
	
	public File getOutputFile(){
		return output;		
	}
	
	public List<Integer> getListOfInt(){
		
		if(list != null)
			return list;
		
		if(buf == null)
			mapInputFile();
		
		ArrayList<Integer> array = new ArrayList<Integer>();
		try{
			StringTokenizer st = new StringTokenizer(new String(buf), ", ");
			while(st.hasMoreTokens()){
				array.add(new Integer(st.nextToken()));
			}
		}catch (OutOfMemoryError e) {
        	System.out.println("Input file is too big." + e);
        	System.exit(0);
		}catch(Exception e){
			System.out.println("Wrong format of input file. Only digits, spaces and commas are expected." + 
					" Check encoding of the text(ANSI is expected).");
			System.exit(0);
		}
     		
        list = array;
		return list;
	}
	
	public List<byte[]> getListOfBytes(){
		if(buf == null)
			mapInputFile();
		
		ArrayList<byte[]> list = new ArrayList<byte[]>();
		try{
			int startIndex = 0;	//index of first digit in the number
			for(int i = 0; i < buf.length; i++){
				
				if(buf[i] - '0' >= 0 && buf[i] - '0' <= 9){
					if(i > 0 && buf[i - 1] - ' ' == 0)
						startIndex = i;					
				}
				
				if(i == buf.length - 1){
					if(buf[i] - ' ' == 0 && buf[i - 1] - ' ' != 0)
						list.add(Arrays.copyOfRange(buf, startIndex, i));
					else if(buf[i] - ' ' != 0)
						list.add(Arrays.copyOfRange(buf, startIndex, i + 1));
					continue;
				}
				
				if(buf[i] - ' ' == 0){
					if(i > 0 && buf[i - 1] - ' ' != 0 )
						list.add(Arrays.copyOfRange(buf, startIndex, i));
					continue;
				}
				
				if(buf[i] - '0' < 0 || buf[i] - '0' > 9){
					throw new Exception();					
				}
			}
		}catch (OutOfMemoryError e) {
	    	System.out.println("Input file is too big." + e);
	    	System.exit(0);
		}catch(Exception e){
			System.out.println("Wrong format of input file. Only digits, spaces and commas are expected." + 
					" Check encoding of the text(ANSI is expected).");
			System.exit(0);
		}
		return list;
	}
	
	public MappedByteBuffer mapInputFile(){
		RandomAccessFile raf = null;
		FileChannel chanel = null;
		MappedByteBuffer buffer = null;
		
		//mapping file into memory
		try {
			raf = new RandomAccessFile(input, "r");		    
	        chanel = raf.getChannel();
	        buffer  = chanel.map(MapMode.READ_ONLY, 0, chanel.size()); //map whole file
	        if(chanel.size() > Integer.MAX_VALUE){
	        	System.out.println("Input file is too big.");
	        	System.exit(0);
	        }
	        try{
	        	buf = new byte[(int)chanel.size()]; 
	        }catch (OutOfMemoryError e) {
	        	System.out.println("Input file is too big." + e);
	        	System.exit(0);
			}
	        buffer.get(buf);
//	        buf = buffer.array();
	        chanel.close();
	        raf.close();
		} catch (IOException e) {
			System.out.println("Exception occured while trying to read and map a file");
			e.printStackTrace();
			System.exit(0);
		}
		return buffer;
	}
	
	public List<Integer> getListOfInt2(){
		if(list != null)
			return list;
		
		if(buf == null)
			mapInputFile();
		
		ArrayList<Integer> arr = new ArrayList<Integer>();
        int num = -1;
        for(int i = 0; i < buf.length; i++){
        	if(buf[i] == 32 || i == buf.length - 1){
        		if(num != -1)
        			arr.add(num);
        		num = -1;
        		continue;
        	}
        	num = num == -1 ? buf[i] - 48 : num * 10 + buf[i] - 48;
        }
        list = arr;
		return list;
	}

	public void writeOutput(List<Integer> array){
		//writing output file
		BufferedWriter bf = null;
		try{
			bf = new BufferedWriter(new FileWriter(output));
			for(int i = 0; i < array.size() - 1; i++)
				bf.write(array.get(i) + " ");
			bf.write(array.get(array.size() - 1) + "");
		}catch(IOException ex){
			System.out.println("Exception occured while writing the output file: " + ex.getMessage());
		}finally{
			try{
				bf.flush();
				bf.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	}
	
	public void writeByteOutput(List<byte[]> array){
		
		BufferedOutputStream bos = null;
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(output);
			bos = new BufferedOutputStream(fos);
			for(int i = 0; i < array.size() - 1; i++){
				bos.write(array.get(i));
				bos.write(' ');
			}
			
			if(!array.isEmpty())
				bos.write(array.get(array.size() - 1));
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				bos.flush();
				bos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}

	public void setNewFiles(String inputFileName, String outputFileName){
		//initialize and validate files
		try{
			input = new File(inputFileName);
			output = new File(outputFileName);
			
			//true if file exists and we can read it
			if(!input.canRead()){
				System.out.println("First file doesn't exist or it can't be read");
				System.exit(0);
			}
			
			if(output.exists()){
				if(!output.canWrite()){
					System.out.println("Second file exists and can't be overwritten");
					System.exit(0);
				}
			}
		}catch(SecurityException ex){
			System.out.println("File can't be read or overwritten: " + ex.getMessage());
			System.exit(0);
		}
	}
}















